package com.apobates.forum.core.impl.dao;

import com.apobates.forum.core.dao.PostsDao;
import com.apobates.forum.core.entity.Posts;
import com.apobates.forum.utils.persistence.Page;
import com.apobates.forum.utils.persistence.Pageable;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 *
 * @author xiaofanku
 * @since 20200512
 */
@Repository
public class PostsDaoImpl implements PostsDao{
    @PersistenceContext
    private EntityManager entityManager;
    private final static Logger logger = LoggerFactory.getLogger(PostsDaoImpl.class);
    //https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL
    //Boolean - TRUE | FALSE
    //SELECT e FROM  Employee e WHERE e.active = TRUE
    //private final Integer TRUE = 1;
    //private final Integer FALSE = 0;
    
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public Optional<Boolean> editStatus(long id, boolean status) {
        try {
            int affect = entityManager.createQuery("UPDATE Posts p SET p.status = ?1 WHERE p.id = ?2").setParameter(1, status).setParameter(2, id).executeUpdate();
            return (affect == 1) ? Optional.of(true) : Optional.empty();
        } catch (Exception e) {
            return Optional.empty();
        }
    }
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public Optional<Boolean> edit(long id, String content, long editMemberId, String editMemberNickname) {
        try {
            int affect = entityManager.createQuery("UPDATE Posts p SET p.content = ?1, p.modifyMemberId = ?2, p.modifyMemberNickname = ?3, p.modifyDateTime = ?4 WHERE p.id = ?5 AND p.reply = ?6")
                    .setParameter(1, content)
                    .setParameter(2, editMemberId)
                    .setParameter(3, editMemberNickname)
                    .setParameter(4, LocalDateTime.now())
                    .setParameter(5, id)
                    .setParameter(6, true)
                    .executeUpdate();
            return (affect == 1) ? Optional.of(true) : Optional.empty();
        } catch (Exception e) {
            return Optional.empty();
        }
    }
    
    @Override
    public Page<Posts> findAllByTopic(long topicId, Pageable pageable) {
        final long total = findAllByTopicCount(topicId);
        if (total == 0) {
            return emptyResult();
        }
        TypedQuery<Posts> query = entityManager.createQuery("SELECT p FROM Posts p WHERE p.topicId = ?1 ORDER BY p.id ASC", Posts.class).setParameter(1, topicId);
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        final Stream<Posts> result = query.getResultStream();
        return new Page<Posts>() {
            @Override
            public long getTotalElements() {
                return total;
            }
            
            @Override
            public Stream<Posts> getResult() {
                return result;
            }
        };
    }
    
    private long findAllByTopicCount(long topicId) {
        try {
            return entityManager.createQuery("SELECT COUNT(p) FROM Posts p WHERE p.topicId = ?1", Long.class).setParameter(1, topicId).getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[findAllByTopicCount][PostsDao]", e);
            }
        }
        return 0L;
    }
    
    @Override
    public Page<Posts> findAllReplyByTopic(long topicId, Pageable pageable) {
        final long total = countTopicReply(topicId);
        if (total == 0) {
            return emptyResult();
        }
        TypedQuery<Posts> query = entityManager.createQuery("SELECT p FROM Posts p WHERE p.topicId = ?1 AND p.reply = ?2 ORDER BY p.id ASC", Posts.class).setParameter(1, topicId).setParameter(2, true);
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        final Stream<Posts> result = query.getResultStream();
        return new Page<Posts>() {
            @Override
            public long getTotalElements() {
                return total;
            }
            
            @Override
            public Stream<Posts> getResult() {
                return result;
            }
        };
    }
    
    @Override
    public Page<Posts> findAllReplyByTopic(long topicId, long filterMemeberId, Pageable pageable) {
        final long total = findAllReplyByTopicCount(topicId, filterMemeberId);
        if (total == 0) {
            return emptyResult();
        }
        TypedQuery<Posts> query = entityManager.createQuery("SELECT p FROM Posts p WHERE p.topicId = ?1 AND p.reply = ?2 AND p.memberId = ?3 ORDER BY p.id ASC", Posts.class)
                .setParameter(1, topicId)
                .setParameter(2, true)
                .setParameter(3, filterMemeberId);
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        final Stream<Posts> result = query.getResultStream();
        return new Page<Posts>() {
            @Override
            public long getTotalElements() {
                return total;
            }
            
            @Override
            public Stream<Posts> getResult() {
                return result;
            }
        };
    }
    
    private long findAllReplyByTopicCount(long topicId, long filterMemeberId) {
        try {
            return entityManager.createQuery("SELECT COUNT(p) FROM Posts p WHERE p.topicId = ?1 AND p.reply = ?2 AND p.memberId = ?3", Long.class)
                    .setParameter(1, topicId)
                    .setParameter(2, true)
                    .setParameter(3, filterMemeberId)
                    .getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[findAllReplyByTopicCount][PostsDao]", e);
            }
        }
        return 0L;
    }
    @Override
    public Stream<Posts> findOneByOneFloor(List<Long> topicIdList) {
        if (topicIdList == null || topicIdList.isEmpty()) {
            return Stream.empty();
        }
        return entityManager.createQuery("SELECT p FROM Posts p WHERE p.topicId IN ?1 AND p.reply = ?2", Posts.class).setParameter(1, topicIdList).setParameter(2, false).getResultStream();
    }
    
    @Override
    public Stream<Posts> findAllRecentByTopic(long topicId, LocalDateTime prevDate) {
        return entityManager.createQuery("SELECT p FROM Posts p WHERE p.topicId = ?1 AND p.entryDateTime BETWEEN ?2 AND ?3 AND p.reply = ?4 ORDER BY p.id ASC", Posts.class)
                .setParameter(1, topicId)
                .setParameter(2, prevDate)
                .setParameter(3, LocalDateTime.now())
                .setParameter(4, true)
                .getResultStream();
    }
    
    @Override
    public Stream<Posts> findAllRecentByTopicIgnoreStatus(long topicId, int size) {
        return entityManager.createQuery("SELECT p FROM Posts p WHERE p.topicId = ?1 ORDER BY p.entryDateTime DESC", Posts.class)
                .setParameter(1, topicId)
                .setMaxResults(size)
                .getResultStream();
    }
    
    @Override
    public Stream<Posts> findAllByTopic(long topicId, int size) {
        return entityManager
                .createQuery("SELECT p FROM Posts p WHERE p.topicId = ?1 AND p.reply = ?2 ORDER BY p.entryDateTime DESC", Posts.class)
                .setParameter(1, topicId)
                .setParameter(2, true)
                .setMaxResults(size)
                .getResultStream();
    }
    
    @Override
    public Stream<Posts> findAllByTopic(long topicId, int page, int size) {
        TypedQuery<Posts> query = entityManager.createQuery("SELECT p FROM Posts p WHERE p.topicId = ?1 AND p.reply = ?2 ORDER BY p.floorNumber ASC", Posts.class)
                .setParameter(1, topicId)
                .setParameter(2, true);
        query.setFirstResult((page - 1) * size);
        query.setMaxResults(size);
        return query.getResultStream();
    }
    
    @Override
    public Stream<Posts> findAll(Set<Long> postsIdList) {
        if (postsIdList == null || postsIdList.isEmpty()) {
            return Stream.empty();
        }
        return entityManager
                .createQuery("SELECT p FROM Posts p WHERE p.id IN ?1", Posts.class)
                .setParameter(1, postsIdList)
                .getResultStream();
    }
    
    @Override
    public Optional<Posts> findOneByTopicId(long topicId) { 
        try {
            Posts p = entityManager.createQuery("SELECT p FROM Posts p WHERE p.topicId = ?1 AND p.reply = ?2", Posts.class).setParameter(1, topicId).setParameter(2, false).getSingleResult();
            return Optional.ofNullable(p);
        } catch (javax.persistence.NoResultException e) {
            return Optional.empty();
        }
    }
    
    @Override
    public Posts findOneById(long id) { 
        return entityManager.find(Posts.class, id);
    }
    
    @Override
    public long countTopicReply(long topicId) {
        try {
            return entityManager.createQuery("SELECT COUNT(p) FROM Posts p WHERE p.topicId = ?1 AND p.reply = ?2", Long.class).setParameter(1, topicId).setParameter(2, true).getSingleResult();//20200529
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[countTopicReply][PostsDao]", e);
            }
        }
        return 0L;
    }
    
    @Override
    public long maxFloor(long topicId) {
        try {
            return entityManager.createQuery("SELECT MAX(p.floorNumber) FROM Posts p WHERE p.topicId = ?1", Long.class).setParameter(1, topicId).getSingleResult();
        } catch (javax.persistence.NoResultException e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[PostsDao]获取话题的楼层失败", e);
            }
        }
        return 1L;
    }
    
    @Override
    public long countRepliesSize(long topicId, long memberId) {
        try {
            return entityManager.createQuery("SELECT COUNT(p) FROM Posts p WHERE p.topicId = ?1 AND p.reply = ?2 AND p.memberId = ?3", Long.class)
                    .setParameter(1, topicId)
                    .setParameter(2, true)
                    .setParameter(3, memberId)
                    .getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[countRepliesSize][PostsDao]", e);
            }
        }
        return 0L;
    }
    
    @Override
    public Page<Posts> findAll(Pageable pageable) {
        return emptyResult();
    }
    
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void save(Posts entity) {
        if (entity.getFloorNumber() == -1) {
            entity.setFloorNumber(maxFloor(entity.getTopicId()) + 1);
        }
        entityManager.persist(entity);
    }
    
    @Override
    public Optional<Posts> findOne(Long primaryKey) {
        return Optional.ofNullable(entityManager.find(Posts.class, primaryKey));
    }
    
    @Override
    public Optional<Boolean> edit(Posts updateEntity) {
        return Optional.empty();
    }
    
    @Override
    public Stream<Posts> findAll() {
        return Stream.empty();
    }
    
    @Override
    public long count() {
        return -1L;
    }
}